package com.middleware.mvc.handlerAdapter.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.middleware.mvc.annotations.RequestBody;
import com.middleware.mvc.annotations.RequestParam;
import com.middleware.mvc.annotations.ResponseBody;
import com.middleware.mvc.handler.InvocationHandler;
import com.middleware.mvc.handlerAdapter.HandlerAdapter;
import com.middleware.mvc.model.ModelAndView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class RequestMappingHandlerAdapter implements HandlerAdapter {

    private static final Logger log = LoggerFactory.getLogger(RequestMappingHandlerAdapter.class);

    ObjectMapper objectMapper = new ObjectMapper();
    @Override
    public boolean supports(Object handler) {
        return handler instanceof InvocationHandler;
    }

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        InvocationHandler invocationHandler = (InvocationHandler) handler;
        Method method = invocationHandler.getMethod();
        Object object = invocationHandler.getObject();

        //判断方法上是否有 RequestBody 注解
        if (method.isAnnotationPresent(ResponseBody.class)) {
            List<Object> args = getArgs(request,response,method);
            Object invoke;
            if(args.isEmpty()){
                invoke = method.invoke(object);
            }else {
                Object[] argsArray = args.toArray();
                invoke = method.invoke(object, argsArray);
            }
            response.getWriter().print(objectMapper.writeValueAsString(invoke));
        }else {

            List<Object> args = getArgs(request,response,method);
            Object invoke;
            if(args.isEmpty()){
                invoke = method.invoke(object);
            }else {
                Object[] argsArray = args.toArray();
                invoke = method.invoke(object, argsArray);
            }

            if(invoke instanceof ModelAndView){
                ModelAndView modelAndView = (ModelAndView) invoke;
                Map<String, Object> model = modelAndView.getModel();
                model.forEach(request::setAttribute);
                Object view = modelAndView.getView();
                if(view instanceof String){
                    String str = (String) view;
                    handleView(request,response,str);
                }
            }else {
                String str = (String) invoke;
                handleView(request,response,str);
            }
        }

    }

    private void handleView(HttpServletRequest request, HttpServletResponse response, String str){
        String URL = "http://" + request.getServerName() + ":" +
                request.getServerPort() + "/" + request.getContextPath();
        System.out.println("URL:" + URL);
        // 自定义的前后缀（原本也是在xml中读取）
        String prefix = "";
        String suffix = ".jsp";
        if(str.contains("forward:")){
            //转发
            try {
                request.getRequestDispatcher(prefix + str.replace("forward:","") + suffix).forward(request,response);
            } catch (ServletException | IOException e) {
                throw new RuntimeException(e);
            }
        }
        if(str.contains("redirect:")){
            //重定向
            try {
                response.sendRedirect(URL + prefix +
                        str.replace("redirect:","") + suffix);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        //什么都没有指定，转发
        if(!str.contains("forward:") && !str.contains("redirect:")){
            try {
                request.getRequestDispatcher(prefix + str + suffix).forward(request,response);
            } catch (ServletException | IOException e) {
                throw new RuntimeException(e);
            }

        }
    }

    private List< Object> getArgs(HttpServletRequest request, HttpServletResponse response,Method method){
        List<Object> args = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            // 判断参数是否是HttpServletRequest
            if( parameter.getType() == HttpServletRequest.class){
                args.add(request);
            }else if( parameter.getType() == HttpServletResponse.class){
                args.add(response);
            }else if (parameter.isAnnotationPresent(RequestBody.class)) {
                //解析请求中的请求体json字符串 成 对象
                Object o = null;
                try {
                    o = objectMapper.readValue(request.getInputStream(), parameter.getType());
                } catch (IOException e) {
                    log.error("解析请求中的请求体json字符串 失败", e);
                    throw new RuntimeException(e);
                }
                args.add(o);
            }else if(parameter.isAnnotationPresent(RequestParam.class)){
                RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
                Class<?> type = parameter.getType();
                if(type == String.class){
                    args.add(request.getParameter(requestParam.value()));
                }else if(type == Integer.class){
                    args.add(Integer.parseInt(request.getParameter(requestParam.value())));
                }else if(type == Long.class){
                    args.add(Long.parseLong(request.getParameter(requestParam.value())));
                }else if(type == Double.class){
                    args.add(Double.parseDouble(request.getParameter(requestParam.value())));
                }else if(type == Float.class){
                    args.add(Float.parseFloat(request.getParameter(requestParam.value())));
                }else if(type == Boolean.class){
                    args.add(Boolean.parseBoolean(request.getParameter(requestParam.value())));
                }
            }
        }
        return args;
    }

}
